package com.herr.springboot.self.xxx.common.exception;

import com.herr.springboot.self.xxx.common.util.LogUtils;
import com.herr.springboot.self.xxx.common.util.ResponseUtils;
import com.herr.springboot.self.xxx.common.entity.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.BindingException;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.sql.SQLException;

/**
 * 异常统一处理
 * @author herr05
 */
@RestControllerAdvice
@Slf4j
public class ControllerAdvice {

	/**
	 * 参数校验异常
	 * @param e
	 * @param response
	 * @return
	 */
	// validate校验，json传输抛出MethodArgumentNotValidException
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request, HttpServletResponse response)  {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		return ResponseUtils.handleNotValidException(e);
	}

	// validate校验，非json传输采用不同的HttpMessageConverter，抛出BindException。两者可统一解析处理
	@ExceptionHandler(BindException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public ApiResult handleBindException(BindException e, HttpServletRequest request, HttpServletResponse response)  {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		return ResponseUtils.handleNotValidException(e);
	}

	/**
	 * 自定义业务异常
	 * @param e
	 * @param response
	 * @return
	 */
	@ExceptionHandler(BizException.class)
	@ResponseStatus(HttpStatus.OK)
	public ApiResult handleBizException(BizException e, HttpServletResponse response)  {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		ApiResult apiResult=ApiResult.builder().code(e.getCode()).msg(e.getMessage()).build();
		LogUtils.doAfterException(apiResult);
		return apiResult;
	}

	/**
	 * 自定义鉴权失败异常
	 * @param e
	 * @param response
	 * @return
	 */
	@ExceptionHandler(UnAuthException.class)
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	public ApiResult handleUnAuthException(UnAuthException e, HttpServletResponse response)  {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		ApiResult apiResult=ApiResult.builder().code(HttpStatus.UNAUTHORIZED.value()).msg(e.getMessage()).build();
		LogUtils.doAfterException(apiResult);
		return apiResult;
	}

	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
	public ApiResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request, HttpServletResponse response)  {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		ApiResult apiResult=ApiResult.builder().code(HttpStatus.METHOD_NOT_ALLOWED.value()).msg(e.getMessage()).build();
		LogUtils.doAfterException(apiResult);
		return apiResult;
	}

	/**
	 * 404异常
	 * 为了捕获404异常，需要设置throw-exception-if-no-handler-found: true和add-mappings: false。
	 * 且在配置静态资源映射中（参考WebMvcConfig.java）避免使用/**路径，否则不会生效。
	 * @param e
	 * @param request
	 * @param response
	 * @return
	 */
	@ExceptionHandler(NoHandlerFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	public ApiResult handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request, HttpServletResponse response)  {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		ApiResult apiResult=ApiResult.builder().code(HttpStatus.NOT_FOUND.value()).msg(e.getMessage()).build();
		LogUtils.doAfterException(apiResult);
		return apiResult;
	}

	@ExceptionHandler({SQLException.class, DataAccessException.class, CannotCreateTransactionException.class,BindingException.class})
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public ApiResult handleSqlException(Throwable e, HttpServletResponse response)  {
		log.error(e.getMessage(), e);
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		ApiResult apiResult=ApiResult.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg("数据库异常："+e.getClass().getSimpleName()).build();
		LogUtils.doAfterException(apiResult);
		return apiResult;
	}

	@ExceptionHandler(Throwable.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public ApiResult handleException(Throwable e,HttpServletResponse response)  {
		log.error(e.getMessage(), e);
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		ApiResult apiResult=ApiResult.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg(e.getMessage()).build();
		LogUtils.doAfterException(apiResult);
		return apiResult;
	}
}